Post

Replies

Boosts

Views

Activity

Reply to Appkit without storyboards XCode 26.2
@hieronim-bosch AppKit is my favorite framework, nothing else comes close. Until proven otherwise there is still no better way to make a Mac app IMHO. That said I never got into Storyboards on Mac. In Xcode 26.2 I can still create an AppKit project the "old way" which is New Project -> macOS -> App then change Interface to XIB then you get a project without a storyboard. If you want to go completely without Interface Builder you can but I don't think Apple ever had a template for that setup (well if they did I never noticed or it must have been a really long time ago). You can set the NSApplication delegate in main. #import <Cocoa/Cocoa.h> @interface AppDelegate : NSObject <NSApplicationDelegate> @property (strong) NSWindowController *mainWindowController; @end @implementation AppDelegate -(void)applicationDidFinishLaunching:(NSNotification*)aNotification { NSLog(@"Hello world!"); NSWindow *firstWindow = [[NSWindow alloc]initWithContentRect:NSMakeRect(50.0, 50.0, 400.0, 400.0) styleMask:NSWindowStyleMaskTitled backing:NSBackingStoreBuffered defer:NO]; firstWindow.title = @"Xibless"; self.mainWindowController = [[NSWindowController alloc]initWithWindow:firstWindow]; [self.mainWindowController showWindow:nil]; } @end // main.m int main(int argc, const char * argv[]) { @autoreleasepool { NSApplication *app = [NSApplication sharedApplication]; AppDelegate *appDelegate = [[AppDelegate alloc]init]; app.delegate = appDelegate; [app setActivationPolicy:NSApplicationActivationPolicyRegular]; [app run]; } } and I think you also need to build the menu bar programmatically. IMO there really is no reason to do all this. If you want to avoid Interface Builder I think it's more reasonable to just do the following: Create an Xib project. Keep the MainMenu.xib but inside it delete the window but leave the menu bar. Then in your AppDelegate just make your NSWindowController / NSWIndow in code. Then you're mostly without IB - enough for learning. Or if you want you can build the menu bar (see mainMenu property on NSApplication: https://developer.apple.com/documentation/appkit/nsapplication/mainmenu?language=objc).
4d
Reply to NSURL - Are Cached Resource Values Really Automatically Removed After Each Pass Through the Run Loop?
Also CFURL is documented to have different behavior but CFURL and NSURL aren't they 'toll free bridged' so how does that work if you cast a CFURL to a NSURL? So I just did a dumb little test to answer this. I created a CFURLRef and read kCFURLIsHiddenKey and got false (as expected) so it would cache it. Then on another CFURL instance (that points to the same file) I did the same thing (so it was cache the value). After that I set hidden to YES: NSURL *toNSURL = (__bridge NSURL * _Nonnull)cfURL; [toNSURL setResourceValue:@(YES) forKey:NSURLIsHiddenKey error:nil]; Then back to the other instance I cast that one to another NSURL: NSURL *nsCastCopiedVersion = (__bridge NSURL * _Nonnull)aCopiedCFURL; And read isHidden and it is NO. Read aCopiedCFURL hidden and it is also NO. Then dispatch after dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [URLPrinter printCF:aCopiedCFURL]; [URLPrinter printNS:nsCastCopiedVersion]; }); And got: Print copied CF Not hidden // cached value - real value is true. Print copied NSCF: Hidden // hidden true value So indeed using CFURL instead would seem to avoid this. Learn something new every day. Now if I didn't already have a million lines of code already using NSURL I would just use CF instead (still might) because at least for my app I find dumping cache values at every run loop churn undesirable. I did have a better caching system implemented at some point but seems some of it got dumped it when I implemented a workaround to avoid -removeCacheResourceValue: crashes a couple of years ago. It'll all be back in biz soon. Thanks for the help guys.
Topic: App & System Services SubTopic: General Tags:
5d
Reply to NSURL - Are Cached Resource Values Really Automatically Removed After Each Pass Through the Run Loop?
When you access a property on the main thread, the system checks to see if the cached value is still valid, that is, has the main thread ‘turned’ between when you last got the property and now. I see. IMHO binding cache invalidation to the turning of a run loop while simultaneously recommending accessing -getResourceValue:forKey: on a background queue feels somewhat peculiar but I guess that peculiarity came during that long evolution. The main benefit of caching just until the run loop turns I guess would help if you were continuously calling -getResourceValue:forKey: in loop or in the middle of event handling but I wouldn't expect that to be so common given the recommendation to call -getResourceValue:forKey: off the main thread when possible. The downside with this short lived cache is that other threads can read stale values from -getResourceValue:forKey: calls unless they explicitly clear the cache using -removeCachedResourceValueForKey: etc. but using those methods wasn't always safe but hopefully they are now. I was working on just getting metadata I need with other APIs and caching them but there are some important resource values I need that seemingly are only available through the NSURL apis :)
Topic: App & System Services SubTopic: General Tags:
6d
Reply to NSURL - Are Cached Resource Values Really Automatically Removed After Each Pass Through the Run Loop?
So I've discovered if you create the NSURL on a background thread you can get stale resource values even on the next churn of the run loop. But whose run loop 'owns' the URL resources? The main thread's run loop ? The run loop of the thread the URL was created on? Or does NSURL do something like this: - (BOOL)getResourceValue:(out id _Nullable * _Nonnull)value forKey:(NSURLResourceKey)key error:(out NSError ** _Nullable)error API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); { // do whatever to get the resource value //-- // schedule delete from cache. [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(clearResourceValueForKey:) object:key]; [self performSelector:@selector(clearResourceValueForKey:) withObject:key afterDelay:0.0]; } I don't see how you can tie cached resource value state to a run loop - it doesn't make any sense to me, especially since the documentation advises doing file operations on a background thread/queue because hitting the disk can be slow. What if I'm sorting an array of NSURLs by some metadata on a background thread and the main thread run loop is just happily invalidating the cache resources behind my back? Maybe I'm not understanding something. IMO I think better behavior would be just to cache on first access and keep it until I explicitly invalidate it but I guess I have to use CFURL for that. I think it makes more sense to let the developer flush the cache in response to file coordination or whatever API being used to monitor the URL but I guess I have to build a cache on top of the cache.
Topic: App & System Services SubTopic: General Tags:
1w
Reply to NSURL - Are Cached Resource Values Really Automatically Removed After Each Pass Through the Run Loop?
Thanks for responding. I haven't yet been able to reproduce the issue in a small test project so either they made changes in a macOS update or there is some timing issue and/or way the NSURL is initialized in my real project teases the issue out. But I promise I got stale value for nonexistent NSURLIsHiddenKey until I started explicitly dumping it. In my real project the URL comes for -fileURLWithPath: (and initially created on a background thread). My real project is large and complex so when I have more time I'll have to see if I can isolate the issue in a small sample. Due the request by iOS and the server cache the user may see some discrepancies or additional nuances in how and caching behave, particularly regarding stale values. I'm on macOS. Explicit cache management is recommended, especially when dealing with URLs that may have changed state (e.g., deleted or modified), to ensure your application behaves predictably across different environments and runtime sessions. Also CFURL is documented to have different behavior but CFURL and NSURL aren't they 'toll free bridged' so how does that work if you cast a CFURL to a NSURL? I would certainly prefer explicit cache management. Chucking cached resource values at every churn of the run loop seems silly and IMO could cause unintended performance issues because the next time the app asks for the cache resource value I presume we hit the disk unnecessarily when nothing has changed. Chucking cached values at every churn of the run loop really reduces the benefits of caching in the first place - developers have to cache on top of the cache but you don't know what's cache? The only way to be sure you don't get a stale value is to call -removeCacheResourceValue methods, which again seems to reduce the benefits of NSURL caching in the first place. Also the problem with explicit cache management is I don't know if resource values are shared across NSURL instances. In the past I had a talk with an Apple badged person and I think he told me it was (or could be at that time but I'll have to check notes). At that time I discovered that calls to -removeResourceValueForKey and/or -removeAllCachedResourceValues could cause crashes. Do you know if each NSURL instance gets its own isolated cache or are they still shared by NSURLs that point to the same file (which I don't think would be safe)?
Topic: App & System Services SubTopic: General Tags:
1w
Reply to How can I be notified if another app goes full screen on macOS?
If there is a way to get notified when another app goes full screen, I'm unaware of it. If it isn't appropriate for your status windows to ever be displayed in full screen mode have you tried using NSWindowCollectionBehaviorFullScreenNone on these windows? I have a window that should display in full screen sometimes, and like you I want to be polite and initially hide it. I've been using a hack forever to do this which mostly works but it isn't exactly pretty. It involves creating a hidden window with a certain collectionBehavior like NSWindowCollectionBehaviorFullScreenNone | NSWindowCollectionBehaviorCanJoinAllSpace and basically track if the active space contains this window. If it doesn't I assume the active space contains an app with a full screen window. Again not perfect but it's the best I could think of. If there is a better way to do it, I'd also like to know.
Topic: UI Frameworks SubTopic: AppKit Tags:
1w
Reply to AppKit - Legal to Change a View's Frame in -viewDidLayout?
Thanks a lot for responding. Thanks for the post, it’s hard not seeing the code and how you're setting constrains in code. Would you be so kind to provide your code where you setting the constraints so developers here can see it? [..] it is generally not a good idea (and will lead to crashes) to directly modify the frame or bounds of a view that is managed by Auto Layout in my opinion. Initially I did not involve Autolayout at all explicitly. What I had was a small container view controller with something like this (no explicit constraints): @implementation SmallInfoWrapperViewController -(void)viewDidLayout { [super viewDidLayout]; NSRect bounds = self.view.bounds; CGFloat lineHeight = 1.0; self.separator.frame = NSMakeRect(0.0,bounds.size.height-lineHeight,bounds.size.width,lineHeight); BOOL separatorVisible = !self.separator.isHidden; CGFloat wrappedViewHeight = bounds.size.height; if (separatorVisible) { wrappedViewHeight -= lineHeight; } self.wrappedView.frame = NSMakeRect(0.0,0.0,bounds.size.width, wrappedViewHeight); } @end That was fine when wrappedView was an NSScrollView (which contains an NSOutlineView). The NSScrollView/OutlineView/Clipview triplet comes out of a xib which is many years old that also has no explicit Autolayout constraints. All worked fine. Once wrappedView became an NSGlassEffectView (which now contains the scroll view) that is when Autolayout said it needed to break constraints the system was creating from autoresizing masks and it listed weird fixed width and height constraints. I find Autolayout useful but avoided it in certain situations where it seems unnecessary it could overcomplicate and the task was so easy (like this one) it felt like cutting a stick of butter with a chainsaw. But the system seems to be trying to make Autolayout somewhat of a requirement so rather than try to fight it I just gave in. self.wrappedView.translatesAutoresizingMaskIntoConstraints = NO; [NSLayoutConstraint activateConstraints:@[leading, trailing, bottom, top]]; But I initially forgot to remove self.wrappedView.frame from -viewDidLayout which is when I hit the exception. Fix is to instead modify the constant of the top constraint when separator.isHidden changes instead. So I already knew what caused the issue and how to fix it but the exception: "The window has been marked as needing another Layout Window pass, but it has already had more Layout Window passes than there are views in the window" did make me curious enough to ask this question because it doesn't seem so uncommon/unreasonable to make adjustments in -layout and -viewDidLayout. Something like: -(void)layout { [super layout]; [self.dateLabel sizeToFit]; if (![self doesDateLabelFitAtNaturalWidth]) { [self useShorterDateStyleLayout]; } } Of course the above is just "concept code" but shrinking a control based on available space in certain situations is more straightforward and easier to maintain in manual layout than with Autolayout. Most of the time Autolayout is preferred, I agree. In any case it doesn't feel appropriate for the system to generate width and height constraints from autoresizing masks ever (perhaps I have a naive point of view but that looks like it could cause some real trouble to me). IMO it makes sense to generate leading, trailing, top and bottom constraints but width/height I'm not sure about that. There was like some weird 250 point min width constraint or something and I have no idea how that possibly could have been calculated from autoresizing masks. In another area I had to fix I also ran into a min width constraint conflict when I downsized a window width because the generated constraint from an autoresizing mask somehow created a weird min width 50 constraint that was too big for the really small window size. Overall it now seems somewhat dicey to set frames in -viewDidLayout / layout overrides because even if you aren't using constraints explicitly it is difficult to determine what the system is generating under the covers. What do you mean by legal? What I mean by legal is safe to modify and supported. In my opinion I think there should be a point (like UIKit -viewDidLayoutSubviews) where layout can be safely modified with or without explicit Autolayout constraints to give developers maximum control (even though rarely needed). I don't really understand why the system doesn't permit a more hands off approach like this: self.lockLayoutInvalidation = YES; [self doAutolayoutOnSubTree]; [self callViewDidLayout]; self.lockLayoutInvalidation = NO; Whatever the layout is after viewDidLayout - it is what it is. Any overlapping views or mistakes or whatever leave it on the developer to find and fix but crashing, throwing, etc. seems a bit much.
Topic: UI Frameworks SubTopic: AppKit Tags:
2w
Reply to macOS Tahoe WKWebView - NSPrintOperation EXC_BREAKPOINT with Lots of Error Logging
Thanks for replying. Sure. NSPrintOperation *printOp = [self.webView printOperationWithPrintInfo:[NSPrintInfo sharedPrintInfo]]; if (printOp == nil) { os_log_fault(OS_LOG_DEFAULT, "Nil print operation."); return; } if (self.view.window != nil) { [printOp runOperationModalForWindow:self.view.window delegate:self didRunSelector:@selector(myPrintOperationDidRun:success:contextInfo:) contextInfo:nil]; }
Topic: UI Frameworks SubTopic: AppKit Tags:
2w
Reply to NSPathControl -setURL: crash on macOS Tahoe
Okay so I can't seem to get an actual URL to cause this crash. Maybe there is a race condition. I wonder if you could crash other apps by evicting files from iCloud at just the right time. I am able to reproduce it by swizzling BRCloudPathComponentDisplayMetadata's -initWithDisplayName:suffix:url:icon: and calling the original implementation and passing nil for the displayName parameter which then gets passed to NSConcreteMutableAttributedString -initWithString: and crashes. Neat! Not easy to reproduce 'naturally'. Not sure if there is a way I can intercept the URL before passing it to NSPathControl -setURL: and transform it in a way that avoids this crash but haven't been able to reproduce it on my end naturally so I'm not sure what to look for in the URL. Anyone got any pointers? Otherwise one of these methods is looking like some Pollo Tropical - swizzles on the grill. CloudDocs -[BRCloudPathComponentDisplayMetadata initWithDisplayName:suffix:url:icon:] + 180 (BRCloudPathComponentDisplayMetadata.m:75) CloudDocs -[NSURL(BRCloudPathComponent) br_pathComponentDisplayMetadataWithOptions:]_b
Topic: UI Frameworks SubTopic: AppKit Tags:
3w
Reply to NSpasteboard writeObjects bug in Mojave
Actually if you are using the pasteboardWriterFor.. delegates it will work fine. Unfortunately, NSBrowser has so such API (yet?) so I'm stuck. I have filed a bugreport and it's being invetigated. I filed FB5417493 way back in 2019. This NSBrowser bug still exists on macOS Tahoe 26.2 (so nearly 7 years later, happy new year). I've been avoiding the issue all this time by using the deprecated API: -(BOOL)browser:(NSBrowser*)browser writeRowsWithIndexes:(NSIndexSet*)rowIndexes inColumn:(NSInteger)column toPasteboard:(NSPasteboard*)pasteboard { // Code here.... NSArray <NSURL*>*urls = // get urls for columns/rows.. BOOL didWrite = NO; if (@available(macOS 10.14,*)) { NSArray *filenames = [self fileNamesFrommURLs:urls]; // ironically need to use deprecated API on newer OS's [pasteboard declareTypes:@[NSFilenamesPboardType] owner:self]; didWrite = [pasteboard setPropertyList:filenames forType:NSFilenamesPboardType]; } else { // I can just write urls didWrite = [pasteboard writeObjects:urls]; } return didWrite; } But I raised the deployment target so now I'm getting warning which I guess I have to suppress. There is no way to provide more than one drag image with NSBrowser without using private API because the delegate method only accepts one image: -(NSImage*)browser:(NSBrowser*)browser draggingImageForRowsWithIndexes:(NSIndexSet*)rowIndexes inColumn:(NSInteger)column withEvent:(NSEvent*)event offset:(NSPointPointer)dragImageOffset I'm not sure why there is an "Accepted Answer" on this thread because the accepted answer doesn't answer anything and only asks more questions.
Topic: UI Frameworks SubTopic: AppKit Tags:
Dec ’25
Reply to QLPreviewPanel takes forever to load content preview in macOS 26
I noticed this with QLPreviewView--images for .apps take forever to load. Zip files too and I'm sure there are other file types I don't know about. I filed FB20797520 back in October. Considering making the thumbnail myself for these certain file types that I've found to be slow because this is degrading my app but I've been somewhat overwhelmed by these types of bugs I really don't want to write another one of these ugly workarounds. Kind of hoping they just fix it in a timely fashion. Fingers crossed. My app is sandboxed, and access to the files is made through the NSOpenPanel first (Macintosh HD folder). I have non-sandboxed app version of an app and QLPreviewView still takes around 30 seconds to load app icon images. In another part of my app I don't use QLPreviewView but load thumbnails with QLThumbnailGenerator. QLThumbnailGenerator seems to load the thumbnail images quickly.
Topic: UI Frameworks SubTopic: AppKit Tags:
Dec ’25